#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define FTOK_PATH "/dev/zero"//ftok函数第一个参数,因为每个文件的索引号都不一样，而我们需要一个未被使用过的key_t类型的关键字ID，使用该参数的作用仅仅只是为了得到一个独一无二的的索引号,使用不管选哪个文件路径都无所谓。
#define FTOK_PROJID 0x23//ftok函数第二个参数，第一个参数的索引号是一个已经存在的独一无二的文件，再与第二个参数组合就能得到一个新的且独一无二的的关键字。

union semun//信号量集(自我理解：信号量可以是整数，也可以是semid_ds类型结构体，还可以是无符号短整形)
	   //semctl函数第四个参数
	   //作为信号量
{ int val;
 struct semid_ds *buf;
 unsigned short *arry;
};

int semaphore_init(void);//自定义的函数，初始化信号量
int semaphore_p(int semid);//进行p操作的函数
int semaphore_v(int semid);//进行v操作的函数
void semaphore_term(int semid);//信号量移除函数

int main(int argc, char **argv)
{
 int semid;//信号量键值
 pid_t pid;
 int i;

 if( (semid=semaphore_init()) < 0)//初始化信号量，返回信号量值
 {
 printf("semaphore initial failure: %s\n", strerror(errno));
 return -1;
 }
 
 if( (pid=fork()) < 0)
 {
 printf("fork() failure: %s\n", strerror(errno));
 return -2;
 }

 else if( 0 == pid) /* child process */
 {
 printf("Child process start running and do something now...\n");
 sleep(3);
 printf("Child process do something over...\n");
 semaphore_v(semid);//进行v操作
 sleep(1);
 printf("Child process exit now\n");
 exit(0);
 }
 
 /* 前面的semaphore_init()函数里将信号量的值设为0，如果这时候父进程先执行的话，P操作会阻塞。直到子进程执行V操作
后，父进程的P操作才能返回继续执行 */
 printf("Parent process P operator wait child process over\n");
 semaphore_p(semid);//进行p操作
 printf("Perent process destroy semaphore and exit\n");
 sleep(2);
 printf("Child process exit and ");
 semaphore_term(semid);//移除信号量
 return 0;
}




int semaphore_init(void)
{
 key_t key;//关键字变量
 int semid;//信号量键值
 union semun sem_union;//该联合变量是全局变量
 
 if( (key=ftok(FTOK_PATH, FTOK_PROJID)) < 0 )//返回值为两个参数的运算：第一个参数指定的文件的索引节点号换算成十六进制后，再将第二个参数换算成十六进制，然后把第二个参数值放到第一个参数值的前面。
 { 
 printf("ftok() get IPC token failure: %s\n", strerror(errno));
 return -1;
 }
 
 semid = semget(key, 1, IPC_CREAT|0644);//获取信号量：参数一：关键字；参数二：创建信号量的个数；参数三：
 if( semid < 0)
 {
 printf("semget() get semid failure: %s\n", strerror(errno));
 return -2; }
printf("semid=%d\n",semid);
 sem_union.val = 0;//对联合变量的val成员赋值,也就是信号量,该信号量是整形。

 if( semctl(semid, 0, SETVAL, sem_union)<0 )//第一个参数为信号量键量；第二个参数为信号量集中的哪一个成员，即union semun中的成员（0为第一个成员：val； 1为第二个成员：semid_ds *buf;以此类推）；第三个参数为对信号量集处理；第四个参数为信号量集。
 {
 printf("semctl() set initial value failure: %s\n", strerror(errno));
 return -3;
 }
 printf("Semaphore get key_t[0x%x] and semid[%d]\n", key, semid);
 return semid;
}




void semaphore_term(int semid)//移除信号量
{
 union semun sem_union;
 
 if( semctl(semid, 0, IPC_RMID, sem_union)<0 )//删除信号量集合
 {
 printf("semctl() delete semaphore ID failure: %s\n", strerror(errno));
 }
 return ;
}

int semaphore_p(int semid)//自定义p操作封装函数
{
 struct sembuf _sembuf;
 _sembuf.sem_num = 0;//要对信号量集哪一个成员操作：这里0为信号量集的第一个成员，即semun.val
 _sembuf.sem_op = -1;//p操作，把信号量减一，之后v操作会才不会阻塞
 _sembuf.sem_flg = SEM_UNDO; // IPC_NOWAIT SEM_UNDO：设置为阻塞模式
 if( semop(semid, &_sembuf, 1) < 0 )//pv操作函数
 {
 printf("semop P operator failure: %s\n", strerror(errno));
 return -1;
 }
 return 0;
}
int semaphore_v(int semid)//自定义v操作封装函数
{
 struct sembuf _sembuf;
 _sembuf.sem_num = 0;
 _sembuf.sem_op = 1;//v操作，把信号量加一，之后p操作才不会阻塞
 _sembuf.sem_flg = SEM_UNDO; // IPC_NOWAIT SEM_UNDO
 if( semop(semid, &_sembuf, 1) < 0 )
 {
 printf("semop V operator failure: %s\n", strerror(errno));
 return -1;
 }
 return 0;
}
